# _*_ coding: utf-8 _*_
"""
Time:     2024/8/28
Author:   2051261
File:     check_yml.py
"""
import logging
import re
from tabulate import tabulate
from typing import List, Dict, Any

from extractor.flow.node_model import node_name_flow
from extractor.const import ConstKey
from extractor.const import YmlDBConst

logger = logging.getLogger(__name__)


class InvalidConfigurationError(Exception):
    pass


# 定义必需的字段和有效的数据库类型
REQUIRED_KEYS = {
    YmlDBConst.CONN_NAME, YmlDBConst.DATABASE_TYPE, YmlDBConst.DATABASE_NAME,
    YmlDBConst.DATABASE_IP, YmlDBConst.DATABASE_PORT, YmlDBConst.USER, YmlDBConst.PASSWORD
}

VALID_DB_TYPES = {ConstKey.MYSQL, ConstKey.MSSQL}


def _validate_ip(ip: str) -> bool:
    # 验证IP地址格式 (IPV4)
    ip_pattern = re.compile(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$")
    return bool(ip_pattern.match(ip))


def _validate_port(port) -> bool:
    # 验证端口号是否为整数
    return isinstance(port, int) and 0 <= port <= 65535


async def check_db_yml(db_configurations: List[Dict[str, Any]]):
    for config in db_configurations:
        # 检查是否有缺少的必需字段
        for key in REQUIRED_KEYS:
            if key not in config:
                raise InvalidConfigurationError(
                    f"Missing required key '{key}' in connection '{config.get('conn_name', 'unknown')}'"
                )

        # 验证数据库类型
        if config[YmlDBConst.DATABASE_TYPE] not in VALID_DB_TYPES:
            raise InvalidConfigurationError(
                f"Invalid database type '{config[YmlDBConst.DATABASE_TYPE]}' "
                f"in connection '{config[YmlDBConst.CONN_NAME]}'"
            )

        # 验证IP地址格式
        if not _validate_ip(config[YmlDBConst.DATABASE_IP]):
            raise InvalidConfigurationError(
                f"Invalid IP address '{config[YmlDBConst.DATABASE_IP]}' in connection '{config[YmlDBConst.CONN_NAME]}'"
            )

        # 验证端口号
        if not _validate_port(config[YmlDBConst.DATABASE_PORT]):
            raise InvalidConfigurationError(
                f"Invalid port '{config[YmlDBConst.DATABASE_PORT]}' in connection '{config[YmlDBConst.CONN_NAME]}'"
            )
    logger.info("DB YAML passed all checks.")
    return True


def check_task_yml(files: list[str], task_configurations: List[Dict[str, Any]]):
    task_names = set()
    error_records = []  # 存储每个配置的错误信息
    all_passed = True  # 追踪是否所有配置都通过

    for file_name, config in zip(files, task_configurations):
        task_name = config.get('task_name')
        task_result = {"File Name": file_name, "Task Name": task_name or "Unknown", "Status": "Passed", "Error": ""}

        if not task_name:
            task_result["Status"] = "Failed"
            task_result["Error"] = "Missing or empty 'task_name':"
            error_records.append(task_result)
            all_passed = False
            continue  # 跳过这个配置继续下一个

        if task_name in task_names:
            task_result["Status"] = "Failed"
            task_result["Error"] = f"Duplicate 'task_name': {task_name}."
            error_records.append(task_result)
            all_passed = False
            continue

        task_names.add(task_name)
        flow = config.get('flow', [])

        for node in flow:
            node_name = node.get('node_name')
            method = node_name_flow[node_name]
            if method is None:
                task_result["Status"] = "Failed"
                task_result[
                    "Error"] = f"task_name: {task_name} is not implement '{node_name}'."
                error_records.append(task_result)
                all_passed = False
                break  # 不再检查当前任务的其他节点

            if not callable(method):
                task_result["Status"] = "Failed"
                task_result["Error"] = f"task_name: {task_name} '{node_name}' is not callable."
                error_records.append(task_result)
                all_passed = False
                break

        if task_result["Status"] == "Passed":
            error_records.append(task_result)

    # 输出结果表格
    table = tabulate(error_records, headers="keys", tablefmt="grid")
    logger.info(table)
    print(table)

    if not all_passed:
        logger.error("One or more tasks failed validation.")
        raise InvalidConfigurationError("One or more tasks failed validation. See log for details.")

    logger.info("All tasks passed validation.")
    return True
